package com.google.io.lastcall.gwt.countdownclock.client;

import java.util.Date;

import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootPanel;


public class CountDownClock implements EntryPoint {

	private PopupPanel popup;
	private MatrixGrid[] grids;
	private int years,days,hours,minutes,seconds;
	private long eventMs;
	private Timer updateInterval=null;
	private Timer drawInterval=null;
	private boolean pastLeap=false;
	private Canvas[] canvas;
	private Context2d[] contexts;
	private int canvasWidth=200;
	private int canvasHeight=200;
	private static final int columns=10;
	private static final int rows=11;
	private boolean skip[][][];
	private boolean bigText[];
	private int[][] touched;
	private static final int NUM_CANVAS=5;
	private boolean[] isOver;
	private Label label;
  /**
   * Entry point method.
   */
  public void onModuleLoad() {
	  start();
  }
  private void start() {
	  skip=new boolean[NUM_CANVAS][columns][rows];
	  canvas=new Canvas[NUM_CANVAS];
	  contexts=new Context2d[NUM_CANVAS];
	  grids=new MatrixGrid[NUM_CANVAS];
	  bigText=new boolean[NUM_CANVAS];
	  isOver=new boolean[NUM_CANVAS];
	  touched=new int[NUM_CANVAS][2];
	  for(int i=0;i<NUM_CANVAS;i++) {
		canvas[i] = Canvas.createIfSupported();
      	canvas[i].setStyleName("mainCanvas");
      	canvas[i].setWidth(canvasWidth + "px");
      	canvas[i].setCoordinateSpaceWidth(canvasWidth);
      	canvas[i].setHeight(canvasHeight + "px");
      	canvas[i].setCoordinateSpaceHeight(canvasHeight);
      	final int column=i;
      	canvas[i].addMouseOverHandler(new MouseOverHandler() {
			@Override
			public void onMouseOver(MouseOverEvent event) {
				isOver[column]=true;
			}
      	});
      	canvas[i].addMouseOutHandler(new MouseOutHandler() {
			@Override
			public void onMouseOut(MouseOutEvent event) {
				isOver[column]=false;
			}
      	});
      	canvas[i].addKeyDownHandler(new KeyDownHandler() {
			@Override
			public void onKeyDown(KeyDownEvent event) {
				if(event.isUpArrow()) {
					int height=(Math.abs(canvas[column].getOffsetHeight()-20)+10);
					canvas[column].setHeight(height+"px");
					canvas[column].setCoordinateSpaceHeight(height);
				} else if(event.isDownArrow()) {
					int height=Math.abs(canvas[column].getOffsetHeight()+10);
					canvas[column].setHeight(height+"px");
					canvas[column].setCoordinateSpaceHeight(height);
				} else if(event.isRightArrow()) {
					int width=Math.abs(canvas[column].getOffsetWidth()+10);
					canvas[column].setWidth(width+"px");
					canvas[column].setCoordinateSpaceWidth(width);
				} else if(event.isLeftArrow()) {
					int width=(Math.abs(canvas[column].getOffsetWidth()-20)+10);
					canvas[column].setWidth(width+"px");
					canvas[column].setCoordinateSpaceWidth(width);
				}
				int canvasWidth=canvas[column].getCoordinateSpaceWidth();
				int canvasHeight=canvas[column].getCoordinateSpaceHeight();
				contexts[column].clearRect(0,0,canvasWidth,canvasHeight);
				contexts[column].setFillStyle("#101010");
				contexts[column].fillRect(0,0,canvasWidth,canvasHeight);
				contexts[column].setFillStyle("#00DD00");
				int size=canvasWidth/10;
				contexts[column].setFont(size+"px sans-serif");
				contexts[column].fillText("resizing...",1,(canvasHeight/2)-(size/2));
			}
      	});
      	canvas[i].addKeyUpHandler(new KeyUpHandler() {
			@Override
			public void onKeyUp(KeyUpEvent event) {
				draw(column);	
			}
      	});
      	canvas[i].addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				int canvasWidth=canvas[column].getOffsetWidth();
				int canvasHeight=canvas[column].getOffsetHeight();
				float f=(float)event.getRelativeX(canvas[column].getElement())/(float)canvasWidth;
				final int x=(int)(f*(float)columns);
				bigText[column]=true;
				grids[column].stopped[x]=!grids[column].stopped[x];
				if(x>0) {
					grids[column].stopped[x-1]=!grids[column].stopped[x-1];
				}
				if(x<columns-1) {
					grids[column].stopped[x+1]=!grids[column].stopped[x+1];
				}
				f=((float)event.getRelativeY(canvas[column].getElement())/(float)canvasHeight);
				final int y=(int)(f*(float)rows);
				touched[column][0]=event.getRelativeX(canvas[column].getElement());
				touched[column][1]=event.getRelativeY(canvas[column].getElement());
				for(int i=0;i<columns;i++) {
					grids[column].SPEEDS[i]/=2;
				}
				skip[column][x][y]=true;
				if(y>0) {
					skip[column][x][y-1]=true;
				}
				if(y<rows-1) {
					skip[column][x][y+1]=true;
				}
				if(x>0) {
					skip[column][x-1][y]=true;
					if(y>0) {
						skip[column][x-1][y-1]=true;
					}
					if(y<rows-1) {
						skip[column][x-1][y+1]=true;
					}
				}
				if(x<columns-1) {
					skip[column][x+1][y]=true;
					if(y>0) {
						skip[column][x+1][y-1]=true;
					}
					if(y<rows-1) {
						skip[column][x+1][y+1]=true;
					}
				}
				Timer t=new Timer() {
					@Override
					public void run() {
						bigText[column]=false;
						for(int i=0;i<columns;i++) {
							grids[column].SPEEDS[i]*=2;
						}
						skip[column][x][y]=false;
						if(y>0) {
							skip[column][x][y-1]=false;
						}
						if(y<rows-1) {
							skip[column][x][y+1]=false;
						}
						if(x>0) {
							skip[column][x-1][y]=false;
							if(y>0) {
								skip[column][x-1][y-1]=false;
							}
							if(y<rows-1) {
								skip[column][x-1][y+1]=false;
							}
						}
						if(x<columns-1) {
							skip[column][x+1][y]=false;
							if(y>0) {
								skip[column][x+1][y-1]=false;
							}
							if(y<rows-1) {
								skip[column][x+1][y+1]=false;
							}
						}
					}
				};
				t.schedule(2000);
			}
      	});
	  	contexts[i]=canvas[i].getContext2d();
	  }
	  popup=new PopupPanel();
	  popup.setHeight("90%");
	  popup.setPopupPosition(25, 25);
	  popup.add(new ChooseDate(new MyHandler() {
		@SuppressWarnings("deprecation")
		@Override
		public String receive(final String info) {
			RootPanel.get("MYpopup").remove(popup);
			popup=null;
			Date date=new Date(info);
			label.setText("Counting Down to: "+date.toString());
			eventMs=date.getTime();
			int tempMonth=date.getMonth();
			Date date2=new Date();
			int tempMonth2=date2.getMonth();
			pastLeap=false;
			if((tempMonth>=2||(tempMonth==1&&date.getDate()==29))&&((date.getYear()>date2.getYear())||(tempMonth2<=1&&!(tempMonth2==1&&date2.getDate()==29)))) pastLeap=true;
			updateTime();
			if(updateInterval!=null) {
				updateInterval.cancel();
			}
			for(int i=0;i<NUM_CANVAS;i++) {
				grids[i]=new MatrixGrid(columns,rows,' ');
			}
			updateInterval=new Timer() {
				@Override
				public void run() {
					updateTime();
				}
			};
			if(drawInterval!=null) {
				drawInterval.cancel();
			}
			drawInterval=new Timer() {
				@Override
				public void run() {
					draw();
				}
			};
			updateInterval.scheduleRepeating(1000);
			drawInterval.scheduleRepeating(250);
			return info;
		}
	  }));
	  RootPanel.get("MYpopup").add(popup);
	  HorizontalPanel holder=new HorizontalPanel();
	  for(int i=0;i<canvas.length;i++) {
		  holder.add(canvas[i]);
	  }
	  Anchor a=new Anchor();
	  a.addClickHandler(new ClickHandler() {
		  @Override
		  public void onClick(ClickEvent event) {
			  if(popup==null) {
					drawInterval.cancel();
					drawInterval=null;
					updateInterval.cancel();
					updateInterval=null;
				  start();
			  }
		  } 
	  });
	  a.setHref("#");
	  a.setText("Choose New Date");
	  label=new Label("Counting Down to: ");
	  RootPanel.get("MYcountdownclock").clear();
	  RootPanel.get("MYcountdownclock").add(holder);
	  RootPanel.get("MYcountdownclock").add(label);
	  RootPanel.get("MYcountdownclock").add(a);
  }
  private void draw() {
	  for(int j=0;j<grids.length;j++) {
			grids[j].run();
			draw(j);	
		}
  }
  private void draw(int j) {
	  	int canvasWidth=canvas[j].getCoordinateSpaceWidth();
		int canvasHeight=canvas[j].getCoordinateSpaceHeight();
		contexts[j].clearRect(0,0,canvasWidth,canvasHeight);
		contexts[j].setFillStyle(isOver[j]?"#101010":"#000000");
		contexts[j].fillRect(0,0,canvasWidth,canvasHeight);
		MatrixNode[][] mn=grids[j].iterator();
		contexts[j].setShadowBlur(canvasWidth/4);
		contexts[j].setShadowColor("#00AA00");
		contexts[j].setFillStyle("#55CC55");
		int width=canvasWidth/4;
		if(bigText[j]) width+=Math.floor(Math.random()*((canvasWidth/5)-10))+10;
		contexts[j].setFont("bold "+width+"pt sans-serif");
		int pad=getPad(j);
		width=width*pad;
		int amount=getAmount(j);
		if(bigText[j]) {
			int radius=(int) (Math.floor(Math.random()*(canvasWidth/3))+5);
			contexts[j].setStrokeStyle("006600");
			contexts[j].setLineWidth(canvasWidth/20);
			contexts[j].beginPath();
			contexts[j].arc(touched[j][0],touched[j][1],radius,0,Math.PI*2);
			contexts[j].stroke();
		}
		contexts[j].fillText(pad(amount,pad),(canvasWidth/2)-(width/2),(canvasHeight/2));
		width=canvasWidth/6;
		contexts[j].setFont("bold "+width+"pt sans-serif");
		String st=getText(j);
		width=width*st.length();
		int offset=0;
		if(bigText[j]) {
			offset=(int) (Math.floor(Math.random()*(canvasWidth/10))+1);
		}
		contexts[j].fillText(st,(canvasWidth/2)-(width/2),(canvasHeight/2)+(canvasWidth/4)+offset);
		contexts[j].setShadowBlur(5);
		for(int i=0;i<mn.length;i++) {
			if(mn[i]==null) continue;
			for(int k=0;k<mn[i].length;k++) {
				if(mn[i][k]==null) continue;
				if(skip[j][i][k]) {
					continue;
				}
				contexts[j].setFillStyle(mn[i][k].color);
				int xStep=canvasWidth/mn.length;
				int x=xStep*mn[i][k].x;
				int yStep=canvasHeight/mn[i].length;
				int y=yStep*mn[i][k].y;
				String str=mn[i][k].isMainChar?pad(amount,pad):Character.toString(mn[i][k].myChar);
				offset=mn[i][k].isMainChar?1:3;
				if(bigText[j]) xStep+=Math.floor(Math.random()*10)+1;
				contexts[j].setFont("bold "+(xStep-(canvasWidth/20))+"pt sans-serif");
				contexts[j].fillText(str,x,y);
				contexts[j].fillText(str,x+offset,y);
				contexts[j].fillText(str,x+offset,y+offset);
				contexts[j].fillText(str,x,y+offset);
				contexts[j].fillText(str,x-offset,y);
				contexts[j].fillText(str,x-offset,y-offset);
				contexts[j].fillText(str,x,y-offset);
			}
		}
  }
  public static native void alert(String msg) /*-{
  $wnd.alert(msg);
}-*/;
  @SuppressWarnings("deprecation")
private void updateTime() {
	Window.setTitle(years+":"+days+":"+hours+":"+minutes+":"+seconds);
	long ms=(eventMs-(new Date()).getTime());
	if(ms<=0) {
		years=days=hours=minutes=seconds=0;
		return;
	}
	long amount=(24l*60l*60l);
	amount*=1000l;
	years=(int)((ms/amount)/365);
	int tempYear=(new Date()).getYear()+1900;
	int offset=0;
	if(!pastLeap) offset=-1;
	ms-=(((long)(years*365))*amount);
	ms+=(amount*(long)leapYears(tempYear,years+tempYear+offset));
	days=(int)(ms/amount);
	ms-=(((long)days)*amount);
	amount/=24;
	hours=(int)(ms/amount);
	ms-=(((long)hours)*amount);
	amount/=60;
	minutes=(int)(ms/amount);
	ms-=(((long)minutes)*amount);
	amount/=60;
	seconds=(int)(ms/amount);
  }
  private static int leapYears(int year,int lastYear) {
	  int count=0;
	  for(int i=year;i<=lastYear;i++) {
		  if(ChooseDate.isLeapYear(i)) count++;
	  }
	  return count;
  }
  private static String pad(int out,int amount) {
	  String output=Integer.toString(out);
	  while(output.length()<amount) {
		  output="0"+output;
	  }
	  return output;
  }
  private int getAmount(int j) {
	  switch(j) {
	  case 0:
		  return years;
	  case 1:
		  return days;
	  case 2:
		  return hours;
	  case 3:
		  return minutes;
	  case 4:
		  return seconds;
	  default:
		  return 0;
	  }
  }
  private int getPad(int j) {
	  switch(j) {
	  case 0:
		  return 2;
	  case 1:
		  return 3;
	  case 2:
		  return 2;
	  case 3:
		  return 2;
	  case 4:
		  return 2;
	  default:
		  return 0;
	  }
  }
  private String getText(int j) {
	  switch(j) {
	  case 0:
		  return "yrs";
	  case 1:
		  return "days";
	  case 2:
		  return "hrs";
	  case 3:
		  return "min";
	  case 4:
		  return "sec";
	  default:
		  return "";
	  }
  }
}